﻿//-----------------------------------------------------------------------
// <copyright file="Engine.cs" company="tuliprules.org">
//     Copyright (c) 2010 tuplelister, tuliprules.org.
// </copyright>
// <summary>This is the Engine class.</summary>
//-----------------------------------------------------------------------

namespace Tulip.Engine
{
    using System.Collections.Generic;
    using EngineOperation;

    /// <summary>
    /// Represents the tuple list processing engine in terms of states and state transitions.  Manages a queue of 
    /// engine operations.  The engine is responsible for pattern match and quantification, and also supports idle
    /// and rule firing states.  It is implemented as a simple finite state automaton.
    /// </summary>
    public class Engine
    {
        /// <summary>
        /// A queue of engine operations.
        /// </summary>
        private readonly Queue<IEngineOperation> actionQueue;

        /// <summary>
        /// The current state of the engine.
        /// </summary>
        private EngineState state;

        /// <summary>
        /// Initializes a new instance of the Engine class.
        /// </summary>
        public Engine()
        {
            this.actionQueue = new Queue<IEngineOperation>();
            this.state = EngineState.Idle;
        }

        /// <summary>
        /// Engine states.
        /// </summary>
        internal enum EngineState
        {
            /// <summary>
            /// The idle state in which the engine is quiesced.
            /// </summary>
            Idle,

            /// <summary>
            /// The matching state in which the engine performs pattern matching.
            /// </summary>
            Matching,

            /// <summary>
            /// The quantifying state in which the engine performs deffered quantification.
            /// </summary>
            Quantifying,

            /// <summary>
            /// The firing state in which the engine fires activated productions on an agenda.
            /// </summary>
            Firing
        }

        /// <summary>
        /// Adds an engine operation to the action queue.
        /// </summary>
        /// <param name="engineOperation">The engine operation to be en-queued.</param>
        public void AddAction(IEngineOperation engineOperation)
        {
            if (engineOperation != null)
            {
                this.actionQueue.Enqueue(engineOperation);
            }
        }

        /// <summary>
        /// Perform a state stransition from one engine state to the next.
        /// </summary>
        /// <param name="facts">A collection of facts.  When the current state is 'firing', if null 
        /// or empty, the engine switches to the idle state.  Otherwise, it switches to the matching
        /// state.</param>
        public void Transition(ref object[] facts)
        {
            // Transition the state
            switch (this.state)
            {
                case EngineState.Idle:
                    this.state = EngineState.Matching;
                    break;
                case EngineState.Matching:
                    this.state = EngineState.Quantifying;
                    break;
                case EngineState.Quantifying:
                    this.state = EngineState.Firing;
                    break;
                case EngineState.Firing:
                    if (facts == null || facts.Length == 0)
                    {
                        this.state = EngineState.Idle;
                    }
                    else
                    {
                        this.state = EngineState.Matching;
                    }

                    break;
            }

            // Exhaust all current queued actions
            while (this.actionQueue.Count > 0)
            {
                if (this.actionQueue.Dequeue().Perform(ref facts))
                {
                    break;
                }
            }
        }
    }
}
